﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using NHibernate.Criterion;
using Light.Utility;
using System.Collections;
using Light.Framework.Castle;

namespace Light.Framework
{
    /// <summary>
    /// 查询参数
    /// </summary>
    public class SearchArgs
    {

        public SearchArgs()
        {
            Extras = new Dictionary<string, object>();
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="keyword">输入的关键字</param>
        /// <param name="searchFields">搜索属性字段列表</param>
        public SearchArgs(string keyword, List<string> searchFields)
            : this()
        {
            if (!String.IsNullOrEmpty(keyword))
            {
                foreach (var field in searchFields)
                {
                    this[field] = keyword;
                }
            }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="keyword">输入的关键字</param>
        /// <param name="searchFields">搜索属性字段字典</param>
        public SearchArgs(string keyword, Dictionary<string, string> searchFields)
            : this()
        {
            if (!String.IsNullOrEmpty(keyword))
            {
                foreach (var key in searchFields.Keys)
                {
                    this[key] = keyword;
                }
            }
        }

        #region private

        private SearchLogic logic = SearchLogic.Or;
        private Dictionary<string, object> fields = new Dictionary<string, object>();
        List<SearchExpression> expressions = new List<SearchExpression>();

        #endregion

        /// <summary>
        /// 设置搜索项，共用属性Logic
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public object this[string name]
        {
            get
            {
                if (fields.Keys.Contains(name))
                    return fields[name];
                else
                    return string.Empty;
            }
            set
            {
                if (fields.Keys.Contains(name))
                    fields[name] = value;
                else
                    fields.Add(name, value);
            }
        }

        /// <summary>
        /// 额外的查询项，调用Add方法的条件会存放于此，仅用于传值
        /// </summary>
        public Dictionary<string, object> Extras { get; set; }

        /// <summary>
        /// 日期：开始
        /// </summary>
        public DateTime DateStart { get; set; }

        /// <summary>
        /// 日期：结束
        /// </summary>
        public DateTime DateEnd { get; set; }

        /// <summary>
        /// 日期：字段名/属性名
        /// </summary>
        public string DateField { get; set; }

        /// <summary>
        ///查询条件逻辑运算符(AND,OR) 默认为OR
        /// </summary>
        public SearchLogic Logic
        {
            get { return logic; }
            set { logic = value; }
        }

        /// <summary>
        /// 逐一添加搜索条件，用单独的SearchLogic
        /// </summary>
        /// <param name="logic"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        public void Add(SearchLogic logic, string field, object value)
        {

            SearchExpression expression = new SearchExpression(logic, field, value);
            expressions.Add(expression);

            Extras[field] = value;
        }

        public void Add(SearchLogic logic, string field, object value, SearchOperator optor)
        {

            SearchExpression expression = new SearchExpression(logic, field, value, optor);
            expressions.Add(expression);

            Extras[field] = value;
        }

        /// <summary>
        /// 移除查询条件
        /// </summary>
        /// <param name="key"></param>
        public void Remove(string key)
        {
            this.fields.Remove(key);
            this.Extras.Remove(key);
            this.expressions.RemoveAll(s => s.PropertyName == key);
        }

        /// <summary>
        /// 转换成NHibernate的查询对象ICriterion
        /// </summary>
        /// <typeparam name="T">要查询的结果类型</typeparam>
        /// <returns>ICriterion</returns>
        public ICriterion ToCriterion<T>()
        {
            ICriterion criterion = null;
            ICriterion criterionDate = null;

            string entityName = typeof(T).Name;
            object value = null;
            string property = string.Empty;

            foreach (var key in this.fields.Keys)
            {
                property = key;
                value = this.fields[key];

                if (value == null || String.IsNullOrEmpty(value.ToString()))
                {
                    continue;
                }

                if (!Light.Utility.ReflectionHelper.TryChangeType<T>(property, ref value))
                {
                    continue;
                }
                else if (value.GetType() == typeof(String))
                {
                    value = "%" + value + "%";
                }
                SearchExpression expression = new SearchExpression(logic, property, value);
                criterion = ConvertExpression(criterion, expression);
            }

            if (criterionDate != null)
            {
                criterion = criterion != null ? Expression.And(criterion, criterionDate) : criterionDate;
            }
            foreach (SearchExpression expresson in expressions)
            {
                criterion = ConvertExpression(criterion, expresson);
            }

            return criterion;
        }

        DetachedCriteria dc = null;
        List<string> alias = new List<string>();

        /// <summary>
        /// 转换成NHibernate的查询对象DetachedCriteria
        /// </summary>
        /// <typeparam name="T">要查询的结果类型</typeparam>
        /// <returns>DetachedCriteria</returns>
        public DetachedCriteria ToDetachedCriteria<T>()
        {
            dc = DetachedCriteria.For<T>();
            ICriterion criterion = null;
            //ICriterion criterionDate = null;

            object value = null;
            string property = string.Empty;

            foreach (var key in this.fields.Keys)
            {
                property = key;
                value = this.fields[key];

                if (value == null || String.IsNullOrEmpty(value.ToString()))
                {
                    continue;
                }
                bool referQuery = key.Contains(".");

                if (!Light.Utility.ReflectionHelper.TryChangeType<T>(property, ref value) && !referQuery)
                {
                    continue;
                }
                else if (value.GetType() == typeof(String))
                {
                    value = "%" + value + "%";
                }

                SearchExpression expression = new SearchExpression(logic, property, value);
                criterion = ConvertExpression(criterion, expression);
            }

            //if (criterionDate != null)
            //{
            //    criterion = criterion != null ? Expression.And(criterion, criterionDate) : criterionDate;
            //}

            foreach (SearchExpression expresson in expressions)
            {
                criterion = ConvertExpression(criterion, expresson);
            }
            if (alias.Count > 0)
            {
                foreach (var name in alias)
                    dc.CreateAlias(name, name);
            }

            if (criterion != null)
            {
                dc.Add(criterion);
            }
            //日期查询条件
            if (!String.IsNullOrEmpty(DateField))
            {
                dc.Add(GetCriterionDate(this.DateField));
            }
            return dc;
        }


        /// <summary>
        /// 获取日期查询条件
        /// </summary>
        /// <returns></returns>
        public ICriterion GetCriterionDate(string propertyName)
        {
            ICriterion criterion = null;

            if (this.DateStart != default(DateTime))
            {
                if (DateEnd != default(DateTime) && DateStart > DateEnd)
                {
                    DateTime tmp = DateStart;
                    DateStart = DateEnd;
                    DateEnd = tmp;
                }
                criterion = Expression.Ge(propertyName, DateHelper.GetStartOfDay(this.DateStart));
            }

            if (this.DateEnd != default(DateTime))
            {
                ICriterion criterionEnd = Expression.Le(propertyName, DateHelper.GetEndOfDay(this.DateEnd));
                if (criterion == null)
                    criterion = criterionEnd;
                else
                    criterion = Expression.And(criterion, criterionEnd);
            }

            if (criterion == null)
                criterion = Expression.Sql("1=1");

            return criterion;
        }


        private ICriterion ConvertExpression(ICriterion criterion, SearchExpression expresson)
        {

            object value = expresson.PropertyValue;
            string property = expresson.PropertyName;

            bool referQuery = property.Contains(".");
            if (referQuery)
            {
                string[] infos = property.Split('.');
                string customeTypeProperty = infos[0];
                string customeTypePropertyProperty = infos[1];
                if (!alias.Contains(customeTypeProperty))
                {
                    alias.Add(customeTypeProperty);
                }
            }

            bool isValueType = value.GetType().BaseType == typeof(System.ValueType);
            bool isDateTime = StringHelper.IsDateTime(value);

            ICriterion criterionObj = Expression.Like(property, value);

            switch (expresson.Operator)
            {
                case SearchOperator.Not:
                    criterionObj = Expression.Not(Expression.Eq(property, value));
                    break;

                case SearchOperator.Gt:
                    criterionObj = Expression.Gt(property, value);
                    break;

                case SearchOperator.Lt:
                    criterionObj = Expression.Lt(property, value);
                    break;

                case SearchOperator.Ge:
                    criterionObj = Expression.Ge(property, value);
                    break;

                case SearchOperator.Le:
                    criterionObj = Expression.Le(property, value);
                    break;

                default:
                    if (isDateTime)
                        criterionObj = Expression.Between(property, DateHelper.GetStartOfDay(StringHelper.ToDateTime(value)), DateHelper.GetEndOfDay(StringHelper.ToDateTime(value)));
                    else
                        criterionObj = isValueType ? Expression.Eq(property, value) : criterionObj;

                    break;
            }

            if (criterion == null)
            {
                criterion = criterionObj;
            }
            else
            {
                if (expresson.Logic == SearchLogic.And || isDateTime)
                    criterion = Expression.And(criterion, criterionObj);
                else
                    criterion = Expression.Or(criterion, criterionObj);
            }
            return criterion;
        }


        #region ToSqlString

        /// <summary>
        /// 是否包含Or查询条件
        /// </summary>
        protected bool HasOr
        {
            get { return fields.Count > 0; }
        }

        /// <summary>
        /// 是否包含And查询条件
        /// </summary>
        protected bool HasAnd
        {
            get { return expressions.Count > 0; }
        }

        /// <summary>
        /// 此SearchArgs中查询条件对应数据库中的列名
        /// </summary>
        /// <returns></returns>
        public string ToSqlString()
        {
            StringBuilder builder = new StringBuilder();
            string sql = GetSqlString();

            if (!String.IsNullOrEmpty(sql))
                builder.Append(" WHERE ");

            builder.Append(sql);

            return builder.ToString();
        }

        private string GetSqlString()
        {
            StringBuilder builder = new StringBuilder();

            //获取 OR 查询部分
            builder.Append(GetOrSql());

            if (HasOr && HasAnd)
                builder.Append(" AND ");

            //获取 AND 查询部分
            builder.Append(GetAndSql());

            //获取 扩展 查询部分
            //builder.Append(builderExt.ToString());

            string sql = builder.ToString();

            if (sql.Trim() != "")
            {
                sql = "(" + sql + ") AND ";
            }
            sql += " IsDeleted=0 ";

            return sql;
        }

        private string GetOrSql()
        {
            StringBuilder builderOr = new StringBuilder();

            // OR 
            if (HasOr)
                builderOr.Append(" (");
            int idx_f = 0;
            foreach (var column in fields.Keys)
            {
                idx_f++;
                string parameterName = FormatParameterName(column, idx_f);
                object value = fields[column];
                if (StringHelper.IsDateTime(value))
                {
                    DateTime input = StringHelper.ToDateTime(value);
                    string start = DateHelper.GetStartOfDay(input).ToString("yyyy-MM-dd HH:mm:ss");
                    string end = DateHelper.GetEndOfDay(input).ToString("yyyy-MM-dd HH:mm:ss");

                    builderOr.Append(column + " BETWEEN '" + start + "' AND '" + end + "'");
                }
                else if (value == null)
                    builderOr.Append(" " + column + " IS NULL");
                else
                    builderOr.Append(column + " LIKE @" + parameterName);

                builderOr.Append(" OR ");
            }

            if (HasOr)
            {
                builderOr.Remove(builderOr.Length - 3, 3);
                builderOr.Append(")");
            }

            return builderOr.ToString();
        }

        private string GetAndSql()
        {
            StringBuilder builderAnd = new StringBuilder();
            // AND
            int idx_e = 0;
            foreach (SearchExpression se in expressions)
            {
                idx_e++;
                //builder2.Append("(");
                builderAnd.Append(" " + se.PropertyName);
                if (se.Operator == SearchOperator.Ge)
                    builderAnd.Append(">=");
                else if (se.Operator == SearchOperator.Gt)
                    builderAnd.Append(">");
                else if (se.Operator == SearchOperator.Le)
                    builderAnd.Append("<=");
                else if (se.Operator == SearchOperator.Lt)
                    builderAnd.Append("<");
                else if (se.Operator == SearchOperator.Not && se.PropertyValue != null)
                    builderAnd.Append("<>");
                //else if (se.Operator == SearchOperator.Like)
                //    builderAnd.Append(" LIKE ");
                else if (se.PropertyValue == null)
                {
                    if (se.Operator == SearchOperator.Not)
                        builderAnd.Append(" IS NOT NULL ");
                    else
                        builderAnd.Append(" IS NULL ");
                }
                //else if (se.Operator == SearchOperator.In && se.PropertyValue is IList)
                //{
                //    builderAnd.Append(" IN (");

                //    IList values = se.PropertyValue as IList;
                //    foreach (var item in values)
                //    {
                //        if (item is Enum || item is int)
                //            builderAnd.Append((int)item + ",");
                //        else if (item is decimal)
                //            builderAnd.Append((decimal)item + ",");
                //        else if (item is double)
                //            builderAnd.Append((double)item + ",");
                //        else if (item is float)
                //            builderAnd.Append((float)item + ",");
                //        else if (item is bool)
                //            builderAnd.Append((((bool)item) ? 1 : 0) + ",");
                //        else
                //            builderAnd.Append("'" + item + "',");
                //    }
                //    builderAnd.Remove(builderAnd.Length - 1, 1);
                //    builderAnd.Append(" ) ");
                //}
                else
                    builderAnd.Append("=");

                if (se.PropertyValue != null && !(se.PropertyValue is IList))
                    builderAnd.Append("@" + FormatParameterName(se.PropertyName, idx_e));
                if (se.Logic == SearchLogic.Or)
                    builderAnd.Append(" OR ");
                else
                    builderAnd.Append(" AND");

            }

            if (!String.IsNullOrEmpty(this.DateField) && (this.DateStart != default(DateTime) || this.DateEnd != default(DateTime)))
            {
                if (this.DateStart != default(DateTime))
                    builderAnd.Append(" " + this.DateField + ">='" + DateHelper.GetStartOfDay(this.DateStart) + "'  AND");
                if (this.DateEnd != default(DateTime))
                    builderAnd.Append(" " + this.DateField + "<='" + DateHelper.GetEndOfDay(this.DateEnd) + "'  AND");
            }

            string temp = builderAnd.ToString();
            if (temp.EndsWith("AND") || temp.EndsWith("OR "))
            {
                builderAnd.Remove(builderAnd.Length - 3, 3);
            }

            return builderAnd.ToString();
        }



        /// <summary>
        /// 获取数据表查询参数
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public List<IDataParameter> GetSqlParameters(ISQLExecuter executer)
        {
            List<IDataParameter> list = new List<IDataParameter>();

            int idx_f = 0;
            foreach (var pName in fields.Keys)
            {
                idx_f++;

                if (fields[pName] == null)
                    list.Add(executer.CreateParameter("@" + FormatParameterName(pName, idx_f), DBNull.Value));
                else if (String.IsNullOrEmpty(fields[pName].ToString()))
                    list.Add(executer.CreateParameter("@" + FormatParameterName(pName, idx_f), string.Empty));
                else if (StringHelper.IsDateTime(fields[pName])) //日期不参数化
                    continue;
                else
                    list.Add(executer.CreateParameter("@" + FormatParameterName(pName, idx_f), "%" + fields[pName] + "%"));
            }

            int idx_e = 0;
            foreach (var se in expressions)
            {
                idx_e++;

                if (se.PropertyValue == null)
                    continue;
                else if (se.PropertyValue is Enum)
                    se.PropertyValue = (int)se.PropertyValue;
                else if (se.PropertyValue is Boolean)
                    se.PropertyValue = (int)se.PropertyValue;

                list.Add(executer.CreateParameter("@" + FormatParameterName(se.PropertyName, idx_e), se.PropertyValue));
            }

            //if (this.parametersExt.Count > 0)
            //    list.AddRange(this.parametersExt);

            return list;
        }


        /// <summary>
        /// 格式化参数名称
        /// </summary>
        /// <param name="inputName"></param>
        /// <returns></returns>
        private string FormatParameterName(string inputName, int idx)
        {
            return inputName.Replace(".", "_") + idx.ToString();
        }



        #endregion

        /// <summary>
        /// 搜索表达式
        /// </summary>
        class SearchExpression
        {
            public SearchExpression(SearchLogic logic, string propertyName, object propertyValue)
            {
                Logic = logic;
                Operator = SearchOperator.Eq;
                PropertyName = propertyName;
                PropertyValue = propertyValue;
            }

            public SearchExpression(SearchLogic logic, string propertyName, object propertyValue, SearchOperator optor)
                : this(logic, propertyName, propertyValue)
            {
                Operator = optor;
            }

            /// <summary>
            /// 条件逻辑
            /// </summary>
            public SearchLogic Logic { get; set; }


            /// <summary>
            /// 条件运算符
            /// </summary>
            public SearchOperator Operator { get; set; }

            /// <summary>
            /// 属性名
            /// </summary>
            public string PropertyName { get; set; }

            /// <summary>
            /// 属性值
            /// </summary>
            public object PropertyValue { get; set; }
        }

    }
}
